#pragma once

#include <iostream>
#include <unistd.h>
#include <wait.h>

#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>

#include "../comm/util.hpp"
#include "../comm/log.hpp"

// 只负责进行代码的编译
namespace ns_compiler
{
    using namespace ns_util;
    using namespace ns_log;

    class Compiler
    {
    public:
        // 编译：成功返回 true，失败返回 false
        static bool Compile(const std::string &file_name) // 参数是一个文件名
        {
            pid_t id = fork();

            if (id < 0)
            {
                LOG(ERROR) << "内部错误，创建子进程失败" << std::endl;
                return false;
            }
            else if (id == 0) // 子进程
            {
                // 子进程主要工作是编译文件，这个可以使用进程替换来实现，
                // g++ 如果执行失败了，会将报错信息打印到 stderr 中，我们不想让它直接打印到 stderr 中，
                // 因为 stderr 默认是终端文件，即终端显示部分，而我们想让错误信息放到一个文件中，
                // 所以我们提前将存放 stderr 信息的文件打开，并进行重定向

                //  1. 打开 stderr 文件
                umask(0);
                int _stderr = ::open(PathUtil::CompilerError(file_name).c_str(), O_CREAT | O_WRONLY, 0644);
                if (_stderr < 0)
                {
                    LOG(WARNING) << "编译 stderr 文件打开失败" << std::endl;
                    exit(1);
                }
                // 2. 重定向 stderr 到 _stderr 文件中
                dup2(_stderr, 2);

                // 3. 程序替换：执行编译指令 g++ -o test.exx test.cc
                execlp("g++", "g++", "-o", PathUtil::Exe(file_name).c_str(),
                       PathUtil::Src(file_name).c_str(), "-D", "COMPILER_ONLINE", "-std=c++11", nullptr); // -D 定义宏，在命令行定义

                LOG(ERROR) << "启动 g++ 编译失败" << std::endl; // 这里直接打印错误是可以的，因为如果程序替换成功了，
                                                                // 执行的代码就会执行 g++ 的代码了，不会执行到这里
                exit(2);
            }
            else // 父进程
            {
                waitpid(id, nullptr, 0);

                // 编译成功：.exe 是否生成了
                if (FileUtil::IsFileExists(PathUtil::Exe(file_name)))
                {
                    LOG(INFO) << PathUtil::Src(file_name) << " 编译成功" << std::endl;
                    return true;
                }
                LOG(ERROR) << "g++ 编译失败，没有形成可执行程序" << std::endl;
                return false;
            }
        }
    };
}
